package it.cums.ui.views.customers;

import it.cums.core.controller.ApplicationController;
import it.cums.core.model.Customer;
import it.cums.core.model.Customers;
import it.cums.core.model.Order;
import it.cums.core.model.Orders;
import it.cums.core.util.CustomerManager;
import it.cums.core.util.MessageCreator;
import it.cums.core.util.OrderManager;
import it.cums.ui.Activator;
import it.cums.ui.actions.NewCustomerAction;
import it.cums.ui.editors.customer.CustomerEditor;
import it.cums.ui.editors.customer.CustomerEditorInput;
import it.cums.ui.editors.order.OrderEditor;
import it.cums.ui.editors.order.OrderEditorInput;
import it.cums.ui.localization.Messages;
import it.cums.ui.util.EditorUtils;
import it.cums.ui.wizards.OrderWizard;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;

public class CustomersView extends ViewPart {
	public static final String ID = "CuMS.CustomersView"; //$NON-NLS-1$

	private TreeViewer viewer;
	private DrillDownAdapter drillDownAdapter;
	private Action deleteAction;
	private Action createCustomerAction;
	private Action createOrderAction;
	private Action doubleClickAction;
	private Action onlyOrdersAction;
	private Action sortByPriorityAction;
	private Customers customers;
	private OrderFilter orderFilter;
	private DueDateSorter dueDateSorter;
	private PrioritySorter prioritySorter;
	private ApplicationController appController = ApplicationController.getInstance();

	class ViewContentProvider implements IStructuredContentProvider, ITreeContentProvider, PropertyChangeListener {

		public void inputChanged(Viewer v, Object oldInput, Object newInput) {
		}
		
		public void dispose() {
		}
		
		public Object[] getElements(Object parent) {
			if (parent.equals(getViewSite())) {
				if (customers==null) initialize();
				return getChildren(customers);
			}
			return getChildren(parent);
		}
		
		public Object getParent(Object child) {
			if (child instanceof Customer) {
				return customers;
			} else if (child instanceof Order) {
				return ((Order)child).getCustomer();
			}
			return null;
		}
		
		public Object[] getChildren(Object parent) {
			if (parent instanceof Customers) {
				return ((Customers)parent).getCustomers().toArray();
			} else if (parent instanceof Customer) {
				Customer customer = (Customer)parent;
				if (customer.getOrders() == null) {
					Orders orders = new Orders(customer);
					orders.addChangeListener(this);
				}
				return customer.getOrders().getOrders().toArray();
			}
			return new Object[0];
		}
		
		public boolean hasChildren(Object parent) {
			return getChildren(parent).length > 0;
		}
/*
 * We will set up a dummy model to initialize tree hierarchy.
 * In a real code, you will connect to a real model and
 * expose its hierarchy.
 */
		private void initialize() {
			customers = appController.getCustomers();
			customers.addChangeListener(this);
			for (Customer customer : customers.getCustomers()) {
				Orders orders = new Orders(customer);
				List<Order> orderList = OrderManager.getInstance().listOrders(customer);
				if (orderList != null) {
					for (Order order : orderList) {
						order.setCustomer(customer);
						orders.addOrder(order);
					}
				}
				orders.addChangeListener(this);
			}
		}

		
		public void propertyChange(PropertyChangeEvent evt) {
			if (onlyOrdersAction.isChecked()) {
				viewer.removeFilter(orderFilter);
			}
			viewer.refresh();
			if (onlyOrdersAction.isChecked()) {
				viewer.addFilter(orderFilter);
			}
		}
	}
	
	class ViewLabelProvider extends LabelProvider {

		public String getText(Object obj) {
			return obj.toString();
		}
		
		public Image getImage(Object obj) {
			String imageKey = "icons/order.gif"; //$NON-NLS-1$
			if (obj instanceof Customer)
			   imageKey = "icons/person.gif"; //$NON-NLS-1$
			return Activator.getImageDescriptor(imageKey).createImage();
		}
	}
	class DueDateSorter extends ViewerSorter {
		
		public void sort(Viewer viewer, Object[] elements) {
			for (Object object : elements) {
				if (object instanceof Customer) {
					Customer customer = (Customer)object;
					if (customer.getOrders() != null) {
						sort(viewer, ((Customer)object).getOrders().getOrders().toArray());
					}
				}
			}
			super.sort(viewer, elements);
		}
		
		
		public int compare(Viewer viewer, Object e1, Object e2) {
			if (e1 instanceof Order) {
				Order order1 = (Order)e1;
				Order order2 = (Order)e2;
				int result =  -order1.getOrderDate().compareTo(order2.getOrderDate());
				if (result == 0) {
					return -order1.toString().compareTo(order2.toString());
				} else {
					return result;
				}
			} 
			return super.compare(viewer, e1, e2);
		}
	}
	
	class PrioritySorter extends ViewerSorter {
		
		public void sort(Viewer viewer, Object[] elements) {
			for (Object object : elements) {
				if (object instanceof Customer) {
					Customer customer = (Customer)object;
					if (customer.getOrders() != null) {
						sort(viewer, ((Customer)object).getOrders().getOrders().toArray());
					}
				}
			}
			super.sort(viewer, elements);
		}
		
		
		public int compare(Viewer viewer, Object e1, Object e2) {
			if (e1 instanceof Order) {
				Order order1 = (Order)e1;
				Order order2 = (Order)e2;
				if (order1.getPriority().equals(order2.getPriority())) {
					return -order1.toString().compareTo(order2.toString());
				} else if (order1.getPriority().equals(Messages.CustomersView_1) && (order2.getPriority().equals(Messages.CustomersView_2) || order2.getPriority().equals(Messages.CustomersView_3))) {
					return -1;
				} else if (order1.getPriority().equals(Messages.CustomersView_5) && order2.getPriority().equals(Messages.CustomersView_4)) {
					return -1;
				} else {
					return 1;
				}
			} 
			return super.compare(viewer, e1, e2);
		}
	}
	
	class OrderFilter extends ViewerFilter {

		
		public boolean select(Viewer viewer, Object parentElement, Object element) {
			if (element instanceof Order) {
				return true;
			}
			return false;
		}
		
		
		public Object[] filter(Viewer viewer, Object parent, Object[] elements) {
			ArrayList<Order> orders = new ArrayList<Order>();
			for (Object object : elements) {
				if (object instanceof Customer) {
					Customer customer = (Customer)object;
					if (customer.getOrders() != null) {
						orders.addAll(customer.getOrders().getOrders());
					}
				}
			}
			return orders.toArray();
		}
		
	}
	
	/**
	 * The constructor.
	 */
	public CustomersView() {
	}

	/**
	 * This is a callback that will allow us
	 * to create the viewer and initialize it.
	 */
	public void createPartControl(Composite parent) {
		viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);
		drillDownAdapter = new DrillDownAdapter(viewer);
		viewer.setContentProvider(new ViewContentProvider());
		viewer.setLabelProvider(new ViewLabelProvider());
		viewer.setInput(getViewSite());
		makeActions();
		hookContextMenu();
		hookDoubleClickAction();
		contributeToActionBars();
		createFiltersAndSorters();
		getSite().setSelectionProvider(viewer);
		viewer.addSelectionChangedListener(new ISelectionChangedListener() {
			
			public void selectionChanged(SelectionChangedEvent event) {
				ISelection selection = viewer.getSelection();
				Object obj = ((IStructuredSelection)selection).getFirstElement();
				if (obj instanceof Customer || obj instanceof Order) {
					deleteAction.setEnabled(true);
				} else {
					deleteAction.setEnabled(false);
				}
			}});
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				CustomersView.this.fillContextMenu(manager);
			}
		});
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, viewer);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		IMenuManager filterSubmenu = new MenuManager(Messages.CustomersView_9);
		manager.add(filterSubmenu);
		filterSubmenu.add(onlyOrdersAction);
		IMenuManager sorterSubmenu = new MenuManager(Messages.CustomersView_10);
		manager.add(sorterSubmenu);
		sorterSubmenu.add(sortByPriorityAction);
		manager.add(new Separator());
		manager.add(deleteAction);
		manager.add(new Separator());
		manager.add(createCustomerAction);
		manager.add(createOrderAction);
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(deleteAction);
		manager.add(createCustomerAction);
		manager.add(createOrderAction);
		manager.add(new Separator());
		drillDownAdapter.addNavigationActions(manager);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}
	
	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(deleteAction);
		manager.add(createCustomerAction);
		manager.add(createOrderAction);
		manager.add(new Separator());
		drillDownAdapter.addNavigationActions(manager);
	}

	private void makeActions() {
		deleteAction = new Action() {
			public void run() {
				ISelection selection = viewer.getSelection();
				Object obj = ((IStructuredSelection)selection).getFirstElement();
				if (obj instanceof Customer) {
					Customer c = (Customer)obj;
					if (MessageCreator.showConfirmMessage(Messages.CustomersView_11 + c.getName() + Messages.CustomersView_12)) {
						boolean successful = CustomerManager.getInstance().deleteCustomer(customers, c);
						if (successful) {
							EditorUtils.closeEditors(c);
						}
					}
				} else {
					Order o = (Order)obj;
					if (MessageCreator.showConfirmMessage(Messages.CustomersView_13 + o.getId() + Messages.CustomersView_14)) {
						boolean successful = OrderManager.getInstance().deleteOrder(o.getCustomer().getOrders(), o);
						if (successful) {
							EditorUtils.closeEditors(o);
						}
					}
				}
			}
		};
		deleteAction.setText(Messages.CustomersView_15);
		deleteAction.setToolTipText(Messages.CustomersView_16);
		deleteAction.setImageDescriptor(Activator.getImageDescriptor("icons/remove.gif")); //$NON-NLS-1$
		deleteAction.setEnabled(false);
		createCustomerAction = new Action() {
			public void run() {
				new NewCustomerAction().run(null);
			}
		};
		createCustomerAction.setText(Messages.CustomersView_18);
		createCustomerAction.setToolTipText(Messages.CustomersView_19);
		createCustomerAction.setImageDescriptor(Activator.getImageDescriptor("icons/person-new.gif")); //$NON-NLS-1$
		createOrderAction = new Action() {
			public void run() {
				OrderWizard wizard = null;
				ISelection selection = viewer.getSelection();
				Object obj = ((IStructuredSelection)selection).getFirstElement();
				if (obj instanceof Customer) {
					wizard = new OrderWizard((Customer)obj);
				} else if (obj instanceof Order) {
					wizard = new OrderWizard(((Order)obj).getCustomer());
				} else {
					wizard = new OrderWizard();
				}
				WizardDialog dialog = new WizardDialog(viewer.getTree().getShell(), wizard);
				dialog.open();
			}
		};
		createOrderAction.setText(Messages.CustomersView_0); 
		createOrderAction.setToolTipText(Messages.CustomersView_22);
		createOrderAction.setImageDescriptor(Activator.getImageDescriptor("icons/order-new.gif")); //$NON-NLS-1$
		
		doubleClickAction = new Action() {
			public void run() {
				ISelection selection = viewer.getSelection();
				Object obj = ((IStructuredSelection)selection).getFirstElement();
				String editorId = ""; //$NON-NLS-1$
				IEditorInput input = null;
				if (obj instanceof Customer) {
					Customer c = (Customer)obj;
					input = new CustomerEditorInput(c);
					editorId = CustomerEditor.ID;
				} else {
					Order o = (Order)obj;
					input = new OrderEditorInput(o);
					editorId = OrderEditor.ID;
				}
				IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
				try {
					page.openEditor(input, editorId);
					// notify any listeners of the view with the actual data of
					// the view
					viewer.setSelection(viewer.getSelection());
				} catch (PartInitException e) {
					Activator.getDefault().logError(e.getMessage(), e);
				}
			}
		};
		onlyOrdersAction = new Action(Messages.CustomersView_25) {
			public void run() {
				updateFilter();
			}
		};
		onlyOrdersAction.setChecked(false);
		sortByPriorityAction = new Action(Messages.CustomersView_26) {
			public void run() {
				updateSorter();
			}
		};
		sortByPriorityAction.setChecked(false);
		
	}

	private void hookDoubleClickAction() {
		viewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				doubleClickAction.run();
			}
		});
	}
	
	private void createFiltersAndSorters() {
		orderFilter = new OrderFilter();
		dueDateSorter = new DueDateSorter();
		viewer.setSorter(dueDateSorter);
		prioritySorter = new PrioritySorter();
	}
	
	private void updateFilter() {
		if (onlyOrdersAction.isChecked()) {
			viewer.addFilter(orderFilter);
			viewer.setSorter(prioritySorter);
		} else {
			viewer.removeFilter(orderFilter);
			viewer.setSorter(dueDateSorter);
		}
	}
	
	private void updateSorter() {
		if (sortByPriorityAction.isChecked()) {
			viewer.setSorter(prioritySorter);
		} else {
			viewer.setSorter(dueDateSorter);
		}
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		viewer.getControl().setFocus();
	}
}